package ru.ifmo.network;

import java.io.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author avhaliullin
 */
public class Downloader {
    private final Logger log = Logger.getLogger(Downloader.class.getName());
    private final InputStream inputStream;
    private final long size;
    private final AtomicLong downloaded = new AtomicLong(0);
    private Throwable exception;
    private AtomicBoolean fileExists = new AtomicBoolean(false);
    private AtomicBoolean cancelled = new AtomicBoolean(false);

    private File destination;
    private String hostName;
    private String sourceFileName;

    public Downloader(InputStream inputStream, long size, String hostName, String sourceFileName) {
        this.inputStream = inputStream;
        this.size = size;
        this.hostName = hostName;
        this.sourceFileName = sourceFileName;
    }

    public void download(final File destination) {
        this.destination = destination;
        new Thread(new Runnable() {
            public void run() {
                BufferedOutputStream out = null;
                try {
                    if (!destination.exists()) {
                        out = new BufferedOutputStream(new FileOutputStream(destination));

                        int len;
                        byte[] buf = new byte[1024];
                        while ((len = inputStream.read(buf)) >= 0 && !cancelled.get()) {
                            Thread.sleep(1);
                            out.write(buf, 0, len);
                            downloaded.addAndGet(len);
                        }
                        out.flush();
                    } else {
                        fileExists.set(true);
                    }
                } catch (IOException e) {
                    log.log(Level.SEVERE, "Can't download file", e);
                    exception = e;
                } catch (InterruptedException e) {
                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                } finally {
                    try {
                        inputStream.close();
                        if (cancelled.get() || exception != null) {
                            destination.delete();
                        }

                        if (out != null) {
                            out.close();
                        }
                    } catch (IOException e) {
                        //ignore
                    }
                }
            }
        }).start();
    }

    public long downloaded() {
        return downloaded.get();
    }

    public long totalSize() {
        return size;
    }

    public Throwable getException() {
        return exception;
    }

    public boolean fileExists() {
        return fileExists.get();
    }

    public void cancel() {
        cancelled.set(true);
    }

    public boolean isCancelled() {
        return cancelled.get();
    }

    public File getDestination() {
        return destination;
    }

    public String getHostName() {
        return hostName;
    }

    public String getSourceFileName() {
        return sourceFileName;
    }
}
